﻿// (c) Copyright slimCODE Software Inc. - www.slimcode.com
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.Text;

namespace SlimCode.SlimPasswords
{
  class Match
  {
    public Match( int index )
    {
      if( index < 0 )
        throw new ArgumentOutOfRangeException( "index", "The index cannot be less than zero." );

      m_index = index;
    }

    #region Index PROPERTY

    private int m_index; // = 0

    public int Index
    {
      get { return m_index; }
    }

    #endregion

    #region NextMatches PROPERTY

    private List<Match> m_nextMatches = new List<Match>();

    public IList<Match> NextMatches
    {
      get { return m_nextMatches; }
    }

    #endregion

    public static IList<Match> FindMatches( string name, string command )
    {
      List<Match> matches = new List<Match>();

      int index = -1;
      string nextCommand = command.Substring( 1 );

      while( ( index = name.IndexOf( command[ 0 ].ToString(), index + 1, StringComparison.InvariantCultureIgnoreCase ) ) != -1 )
      {
        Match match = new Match( index );

        if( Match.FindSubMatches( match, name, nextCommand ) )
        {
          matches.Add( match );
        }
      }

      return matches;
    }

    private static bool FindSubMatches( Match previousMatch, string name, string command )
    {
      if( command.Length == 0 )
        return true;

      bool result = false;

      int index = previousMatch.Index;
      string nextCommand = command.Substring( 1 );

      while( ( index = name.IndexOf( command[ 0 ].ToString(), index + 1, StringComparison.InvariantCultureIgnoreCase ) ) != -1 )
      {
        Match match = new Match( index );

        if( FindSubMatches( match, name, nextCommand ) )
        {
          previousMatch.NextMatches.Add( match );
          result = true;
        }
      }

      return result;
    }

    public int CalculateStrength( string name )
    {
      if( name.Length <= m_index )
        throw new ArgumentException( "The name is smaller than the matching index.", "name" );

      return this.CalculateStrength( name, -1 );
    }

    private int CalculateStrength( string name, int previousIndex )
    {
      int strength = 1;

      // Beginning of name: 4 points.
      // Consecutive letters: 4 points.
      // Beginning of word: 3 points. (!consecutive)
      // Uppercase letter: 1 point.

      if( m_index == previousIndex + 1 )
      {
        // This handles both consecutive letters and beginning of name
        strength += 4;
      }
      else if( ( m_index > 0 ) && ( !char.IsLetterOrDigit( name[ m_index - 1 ] ) ) )
      {
        // Beginning of word
        strength += 3;
      }

      if( char.IsUpper( name[ m_index ] ) )
      {
        // Uppercase
        strength += 1;
      }

      int bestSubStrength = 0;

      foreach( Match match in m_nextMatches )
      {
        int subStrength = match.CalculateStrength( name, m_index );

        if( subStrength > bestSubStrength )
        {
          bestSubStrength = subStrength;
        }
      }

      return strength + bestSubStrength;
    }
  }
}
